Skip to content

Do not read overwritten locals in TupleOptimization - #9213

Open
tlively wants to merge 4 commits into
mainfrom
fix-9210
Open

tlively wants to merge 4 commits into
mainfrom
fix-9210

Conversation

@tlively

@tlively tlively commented Oct 5, 2026 •

Copy link
Copy Markdown
Member

When TupleOptimization splits a tuple local.set into several local.sets,
if a set's value contained a get of a prior tuple element, that value
would previously have incorrectly been the updated rather than original
element. Avoid reading trampled values by copying the original values to
scratch locals before starting to emit the sequence of local sets. Do so
only when there is interference that would make it necessary.

Fixes #9210.

When TupleOptimization splits a tuple local.set into several local.sets,
if a set's value contained a get of a prior tuple element, that value
would previously have incorrectly been the updated rather than original
element. Avoid reading trampled values by copying the original values to
scratch locals before starting to emit the sequence of local sets.

Fixes #9210.
@tlively
tlively requested a review from a team as a code owner October 5, 2026 23:16
@tlively
tlively requested review from kripken and removed request for a team October 5, 2026 23:16
;; CHECK-NEXT: )
;; CHECK-NEXT: (local.set $2
;; CHECK-NEXT: (local.get $4)
;; CHECK-NEXT: )

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is adding extra vars even in trivial cases like this one. How about doing something like ChildLocalizer, conceptually, that is, check for interferences?

Seems like it could be a simple scan of the tuple.make inputs to see that copied fields (from the same tuple) are read in order.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I experimented with emitting scratch locals only as necessary, but I didn't like how complicated it was. Let me see if I can better encapsulate the complexity.

@tlively

tlively commented Oct 7, 2026

Copy link
Copy Markdown
Member Author

@kripken, see the last commit, which adds a utility for determining precisely when scratch locals are necessary. It seems like a good deal of extra complexity, but maybe it's worth it.

@kripken kripken left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nice, I think this is worth the complexity. In some cases large functions have tons of tuples we need to remove, and this will make this pass much faster in that case.

(local.get $t)
)
)
)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please add a test similar to this but where there are two tuples t and u, and the flipping is between them, i.e. not on the same tuple. We should use no temp locals in that case.

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/passes/TupleOptimization.cpp Outdated
const Module& wasm)
: interfering(operands.size(), false) {
Index numOperands = operands.size();
if (numOperands <= 1) {

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This can be an assert, as tuples cannot be of size 1?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done.

Comment thread src/passes/TupleOptimization.cpp Outdated
effects.localsRead.end());
subsequentWrites.insert(effects.localsWritten.begin(),
effects.localsWritten.end());
if (operands[opIndex]->type == Type::unreachable ||

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we need to check unreachable?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We don't; I removed this.

@kripken

kripken commented Oct 7, 2026

Copy link
Copy Markdown
Member

(also it makes the pass output much easier to reason about)

This branch has not been deployed

No deployments
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

TupleOptimization: tuple swap is miscompiled

2 participants